package org.jing.core.socket;

import org.jing.core.logger.JingLogger;
import org.jing.core.thread.itf.BaseMultiplyThread;
import org.jing.core.util.ClassUtil;

import java.lang.reflect.ParameterizedType;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

/**
 * Description: <br>
 *
 * @author bks <br>
 * @since 2020-09-25 <br>
 */
public abstract class BaseSocketServerMenu<T extends BaseSocketServer> extends BaseMultiplyThread<SocketConfiguration> {
    private static final JingLogger LOGGER = JingLogger.getLogger(BaseSocketServerMenu.class);

    public BaseSocketServerMenu() {}

    public BaseSocketServerMenu(int port, int permits) {
        SocketConfiguration config = new SocketConfiguration();
        config.setPort(port);
        config.setPermits(permits);
        super.setConfig(config);
    }

    public void execute() {
        ExecutorService executor = null;
        Class<T> socketServerClazz = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        SocketConfiguration config = getConfig();
        try (ServerSocket server = new ServerSocket(config.getPort())) {
            executor = Executors.newCachedThreadPool();
            int permits = config.getPermits();
            if (permits <= 0) {
                permits = Integer.MAX_VALUE;
            }
            LOGGER.imp("start [port: {}][permits: {}]", config.getPort(), config.getPermits());
            final Semaphore semaphore = new Semaphore(permits);
            Socket client;
            //noinspection InfiniteLoopStatement
            while (true) {
                client = server.accept();
                BaseSocketServer socketServer = ClassUtil.createInstance(socketServerClazz);
                socketServer.init(client, semaphore, config);
                executor.execute(socketServer);
            }
        }
        catch (Throwable t) {
            LOGGER.error(t, "failed to start...");
        }
        finally {
            if (null != executor) {
                executor.shutdown();
            }
        }
    }
}
